package com.boyaa.lxc;

import java.util.Arrays;
import java.util.Random;

import android.util.Log;

/**
 * 扑克的工具类
 * @author Shineflag
 *
 */
public class PokerHands {
	public static final int HAND_NUM = 5;         //手牌的数量
	public static final int TOTAL_OF_CARDS = 7;   //德州扑克共7张牌
	public static final int TOTAL_OF_POKER = 52;  //副扑克的数量
	
	public static final int ROYAL_FLUSH = 1;      //皇家同花顺
	public static final int STRIGHT_FLUSH = 2;    //同花顺
	public static final int FOUR_OF_A_KIND =3;    //四条
	public static final int FULL_HOUSE = 4;       //葫芦
	public static final int FLUSH = 5;            //同花
	public static final int STRIGHT = 6;          //顺子
	public static final int THREE_OF_A_KIND = 7;  //三条
	public static final int TWO_PAIRS = 8;        //两对
	public static final int ONE_PAIRS = 9;        //一对
	public static final int HIGH_CARD =10;        //高牌
	
	/**
	 * 52张扑克数值 
	 */
	public static final int[] POKERS = {
		0x0102, 0x0103, 0x0104, 0x0105, 0x0106, 0x0107, 0x0108, 0x0109, 0x010A, 0x010B, 0x010C, 0x010D, 0x010E,
		0x0202, 0x0203, 0x0204, 0x0205, 0x0206, 0x0207, 0x0208, 0x0209, 0x020A, 0x020B, 0x020C, 0x020D, 0x020E,
		0x0302, 0x0303, 0x0304, 0x0305, 0x0306, 0x0307, 0x0308, 0x0309, 0x030A, 0x030B, 0x030C, 0x030D, 0x030E,
		0x0402, 0x0403, 0x0404, 0x0405, 0x0406, 0x0407, 0x0408, 0x0409, 0x040A, 0x040B, 0x040C, 0x040D, 0x040E
	};
	
	public static final String[] POKER_TYPE = {
		"德州牌型","皇家同花顺","同花顺","四条","葫芦","同花",
		"顺子","三条","两对","一对","高牌"
	};
	/**
	 * 是否同花，0：不是,1:方块diamonds，2：梅花clubs，3：红桃hearts，4：黑桃spades
	 */
	private int flushColor = 0;

	private int pairCount = 0;     //对子数目
	private int threeCount = 0;    //三条数目
	private int fourCount = 0;     //四条数目
	
	private int myType = 0;        //我的牌型 
	
	int [] pairCardValue;
	int [] threeCardValue;
	int  fourCardValue = 0; 
	
	int current_poker_num = 0;    //当前扑克的数量
	
	public PokerCard[] myCards = new PokerCard[HAND_NUM];       //用户的手牌
	public int [] myCardValues = new int[5];     	             //用户的手牌
	public int[] pokers = new int[TOTAL_OF_POKER];  //一幅完整的扑克 （52张）;

	class PokerCard{
		int color;  //扑克的花色
		int value;  //扑克的面值
	}
	
	/**
	 * 洗牌
	 */
	public void shuffle(){
		pokers = POKERS.clone();
		Random random = new Random();
		int pos = 0;
		int tem;
		for(int i=0;i<TOTAL_OF_POKER;i++){
			pos = random.nextInt(TOTAL_OF_POKER);
			tem = pokers[i];
			pokers[i] = pokers[pos];
			pokers[pos] = tem;
		}
		current_poker_num = TOTAL_OF_POKER;
	}
	
	/**
	 * 发牌
	 * @return  发的扑克值 
	 */
	public int dealPoker(){
		if(pokers == null || current_poker_num <=0 || current_poker_num > pokers.length)
			return 0;
		int poker = pokers[current_poker_num-1];
		current_poker_num--;
		return poker;
	}
	public void init(){
		flushColor = 0;
		pairCount = 0;     //对子数目
		threeCount = 0;    //三条数目
		fourCount = 0;     //四条数目
		myType = 0;        //我的牌型 
		

		pairCardValue = new int[3];
		threeCardValue = new int[2];
		fourCardValue = 0; 
		Arrays.fill(myCards, null);
	}
	/**
	 * 比牌
	 * @param cards
	 * @return
	 */
	public String checkHandsType(int [] cards){
		/*牌数小于手牌的数量*/
		if(null == cards || cards.length < HAND_NUM)
			return null;
		init();
		PokerCard [] pokerCards = toPokers(cards);//将数值转换成扑克类型
		if(pokerCards == null)
			return null;
		
		/*德州扑克比牌算法*/
		countFlush(pokerCards); // 统计牌中各花色的数量，检测是否有5张同花色的牌
		sortCards(pokerCards,true);  //将牌从大到小依次排列
		if(flushColor != 0){    //是同花则不可能是四条或葫芦
			if(isStraightFlush(pokerCards)){  //是否同花顺（包括皇家同花顺）
				return POKER_TYPE[myType];
			}else{             //
				filterFlush(pokerCards);     //取出同花
				return POKER_TYPE[myType];
			}
		}else{                  //不是同花
			countSameCards(pokerCards);   //统计牌中复数牌的数量，例如对子、三条或四条
			if(filerFour(pokerCards))
				return POKER_TYPE[myType];
			if(filerThree(pokerCards)){  //判断葫芦或三条
				if(myType == FULL_HOUSE) //如果是葫芦，就不要再判断了
					return POKER_TYPE[myType];
				if(isStraight(pokerCards))  //判断是否为顺子
					return POKER_TYPE[myType];
				return POKER_TYPE[myType];  //三条
			}
			if(isStraight(pokerCards))  //判断是否为顺子
				return POKER_TYPE[myType];
			if(filerTwoPair(pokerCards)){   //判断是否为两对
				return POKER_TYPE[myType];
			}
			if(filerOnePair(pokerCards)){
				return POKER_TYPE[myType];
			}
			filerHighCards(pokerCards);
			
			
		}
		return POKER_TYPE[myType];
	}


	/**
	 * 将扑克类型转换成对应的数值
	 * @param pokerCard
	 * @return
	 */
	public int poker2Int(PokerCard pokerCard){
		if(pokerCard == null)
			return 0;
		int cardValue = pokerCard.color<<8;
		cardValue += pokerCard.value;
		Log.d("POKERHANDS","pokerCard.color="+pokerCard.color+",pokerCard.value="+pokerCard.value+",value="+Integer.toHexString(cardValue));
		return cardValue;
	}
	
	public int [] pokers2Int(PokerCard[] pokerCards){
		if(pokerCards == null)
			return null;
		int [] cards = new int[pokerCards.length];
		for(int i=0;i<cards.length;i++){
			cards[i] = poker2Int(pokerCards[i]);
		}
		return cards;
	}
	
	public int[] getMyCards(){
		return pokers2Int(myCards);
	}
	
	/**
	 * 将值转换成扑克类型
	 * @param card
	 * @return
	 */
	private  PokerCard toPoker(int card){
		PokerCard pokerCard = new PokerCard();
		pokerCard.color = card>>8;
		pokerCard.value = card%256;
		/*错误的扑克值*/
		if(pokerCard.color < 1 || pokerCard.color > 4 || pokerCard.value <2 || pokerCard.value > 14){
			pokerCard.color = 0;
			pokerCard.value = 0;
		}
		Log.d("POKERHANDS","pokerCard.color= "+pokerCard.color+",pokerCard.value="+pokerCard.value);
		return pokerCard;
	}
	
	/**
	 * 将数组转换成扑克数组
	 * @param cards
	 * @return
	 */
	private PokerCard [] toPokers(int [] cards){
		Log.d("POKERHANDS","toPokers");
		if(cards == null || cards.length <= 0)
			return null;
		PokerCard [] pokerCards = new PokerCard[cards.length];
		for(int i = 0;i<cards.length ;i++){
			pokerCards[i] = toPoker(cards[i]);
			if(pokerCards[i] == null)             //如果有扑克是非法数值，则都不处理
				return null;
		}
		return pokerCards;
	}
	
	/**
	 * 判断同花
	 * @param pokerCards
	 */
	private void countFlush(PokerCard [] pokerCards){
		Log.d("POKERHANDS","countFlush");
		if(null == pokerCards)
			return;
		int [] colorCount = new int[5];
		int color;
		for(int i=0; i<pokerCards.length; i++){
			color = pokerCards[i].color;
			if(color > 4)
				color = 0;
			colorCount[color]++;
			if(colorCount[color] >= HAND_NUM){
				flushColor = color;
				return;
			}
		}
	}
	
	/**
	 * 将扑克从大到小排序(选择法）
	 * @param pokerCards
	 */
	public void sortCards(PokerCard [] pokerCards){
		Log.d("POKERHANDS","sortCards");
		if(null == pokerCards)
			return;
		int max;
		PokerCard tem;
		for(int i=0;i<pokerCards.length;i++){
			max = i;
			for(int j=i+1;j<pokerCards.length;j++){
				if(pokerCards[j].value > pokerCards[max].value){
					max = j;
				}
			}
			if(max != i){
				tem = pokerCards[i];
				pokerCards[i] = pokerCards[max];
				pokerCards[max] = tem;
			}
		}
	}
	
	/**
	 * 将扑克从大到小排序(选择法）
	 * @param pokerCards
	 * @param flush  同花色的排前面
	 */
	private void sortCards(PokerCard [] pokerCards,boolean flush){
		Log.d("POKERHANDS","sortCards");
		if(null == pokerCards)
			return;
		int max;
		PokerCard tem;
		for(int i=0;i<pokerCards.length;i++){
			max = i;
			for(int j=i+1;j<pokerCards.length;j++){
				if(pokerCards[j].value > pokerCards[max].value){
					max = j;
				}else if(flush && pokerCards[j].value == pokerCards[max].value && pokerCards[j].color == flushColor){
					max = j;    //方便判断同花顺及取同花顺
				}
			}
			if(max != i){
				tem = pokerCards[i];
				pokerCards[i] = pokerCards[max];
				pokerCards[max] = tem;
			}
		}
	}
	
	/**
	 * 统计牌中复数牌的数量，例如对子、三条或四条
	 * @param pokerCards
	 */
	private void countSameCards(PokerCard [] pokerCards){
		Log.d("POKERHANDS","countSameCards");
		if(null == pokerCards)
			return;
		int value;
		int [] valueCount = new int[13];
		for(int i = 0;i < pokerCards.length;i++){
			value = pokerCards[i].value;
			if( value <2 || value > 14)//牌值的范围为2-14，J:11,Q:12,K:13,A:14
				continue;
			valueCount[value-2]++;
			switch(valueCount[value-2]){
			case 2:
				pairCardValue[pairCount]=value;
				pairCount++;
				break;
			case 3:
				threeCardValue[threeCount]=value;
				threeCount++;
				break;
			case 4:
				fourCardValue = value;
				fourCount++;
				break;
			default:
				break;
			}
			
		}
		
		/*计算出实际的对子、三条的数量*/
		pairCount -= threeCount;
		threeCount -= fourCount;
		Log.d("POKERHANDS","pairCount="+pairCount+",threeCount="+threeCount+",fourCount="+fourCount);
	}
	
	/**
	 * 判断是否是四条
	 * @param pokerCards
	 * @return
	 */
	private boolean filerFour(PokerCard[] pokerCards) {
		Log.d("POKERHANDS","filerFour");
		if(null == pokerCards || pokerCards.length < HAND_NUM)
			return false;
		/*取四条的值 */
		if(fourCount < 1)
			return false;
		for(int i =0,count=0,count2 = 0;i<pokerCards.length;i++){
			if(pokerCards[i].value == fourCardValue && count < 4){
				myCards[count] = pokerCards[i];
				count++;
			}else if(count2 < 1){
				//取一个最大的杂牌
				myCards[HAND_NUM-1]=pokerCards[i];
				count2++;
			}
		}
		myType = FOUR_OF_A_KIND;
		return true;
	}
	
	private boolean filerThree(PokerCard[] pokerCards){
		Log.d("POKERHANDS","filerThree");
		if(null == pokerCards || pokerCards.length < HAND_NUM)
			return false;
		if (threeCount < 0)
			return false;
		/* 取出三条的值 */
		if (threeCount == 2) {
			// 七张牌存在两个三条
			int maxValue=0, minValue=0;
			if (threeCardValue[0] > threeCardValue[1]) {
				maxValue = threeCardValue[0];
				minValue = threeCardValue[1];
			} else {
				maxValue = threeCardValue[1];
				minValue = threeCardValue[0];
			}
			for (int i = 0, count1 = 0, count2 = 0; i < pokerCards.length; i++) {
				if (pokerCards[i].value == maxValue) {
					if (count1 < 3)
						myCards[count1++] = pokerCards[i];
				} else if (pokerCards[i].value == minValue) {
					if (count2 < 2)
						myCards[3 + count2++] = pokerCards[i];
				}
			}
			myType = FULL_HOUSE;
			return true;
		} else if (threeCount == 1) {
			// 可能存在三条、2个两对
			int pairValue = 0;
			if (pairCount > 0) {
				/* 葫芦 */
				for (int i = 0; i <= pairCount; i++) {
					if (pairCardValue[i] != threeCardValue[0]) {
						if (pairValue < pairCardValue[i])
							pairValue = pairCardValue[i];
					}
				}
				for (int i = 0, count1 = 0, count2 = 0; i < pokerCards.length; i++) {
					if (pokerCards[i].value == threeCardValue[0]) {
						if (count1 < 3)
							myCards[count1++] = pokerCards[i];
					} else if (pokerCards[i].value == pairValue) {
						if (count2 < 2)
							myCards[3 + count2++] = pokerCards[i];
					}
				}
				myType = FULL_HOUSE;
				return true;
			} else {
				/* 三条 */
				for (int i = 0, count1 = 0, count2 = 0; i < pokerCards.length; i++) {
					if (pokerCards[i].value == threeCardValue[0]) {
						if (count1 < 3)
							myCards[count1++] = pokerCards[i];
					} else {
						if (count2 < 2)
							myCards[3 + count2++] = pokerCards[i];
					}
				}
				myType = THREE_OF_A_KIND;
				return true;
			}
		}
		
		return false;
	}
	
	/**
	 * 判断是否为两对
	 * @param pokerCards
	 * @return
	 */
	private boolean filerTwoPair(PokerCard[] pokerCards) {
		Log.d("POKERHANDS","filerTwoPair");
		if(null == pokerCards || pokerCards.length < HAND_NUM)
			return false;
		if (pairCount < 2)
			return false;
		int maxPairValue=0,minPairValue=0;
		for (int i = 0; i < pairCount; i++) {      //从两对或三对里选出最大的2pair
			if(pairCardValue[i] > maxPairValue){
				minPairValue = maxPairValue;
				maxPairValue = pairCardValue[i];
			}else if(pairCardValue[i] > minPairValue){
				minPairValue = pairCardValue[i];
			}
		}
		for (int i = 0, count1 = 0, count2 = 0,count3 =0; i < pokerCards.length; i++) {
			if (pokerCards[i].value == maxPairValue) {
				if (count1 < 2)
					myCards[count1++] = pokerCards[i];
			} else if (pokerCards[i].value == minPairValue) {
				if (count2 < 2)
					myCards[2 + count2++] = pokerCards[i];
			} else if(count3 <1 ){
				myCards[HAND_NUM-1] = pokerCards[i];
				count3++;
			}
		}
		myType = TWO_PAIRS;

		return true;
	}
	
	/**
	 * 判断是否为一对
	 * @param pokerCards
	 * @return
	 */
	private boolean filerOnePair(PokerCard[] pokerCards) {
		Log.d("POKERHANDS","filerOnePair");
		if(null == pokerCards || pokerCards.length < HAND_NUM)
			return false;
		if(pairCount <1)
			return false;
		for(int i=0,count1=0,count2=0;i<pokerCards.length;i++){
			if(count1 <2 && pokerCards[i].value == pairCardValue[0] ){
				myCards[count1++] = pokerCards[i];
			}else if(count2 <3){
				myCards[2+count2++] = pokerCards[i];
			}
		}
		myType = ONE_PAIRS;
		return true;
	}
	

	private void filerHighCards(PokerCard[] pokerCards) {
		Log.d("POKERHANDS","filerHighCards");
		if(null == pokerCards || pokerCards.length < HAND_NUM)
			return;
		for(int i = 0;i<HAND_NUM;i++){
			myCards[i] = pokerCards[i];
		}
		myType = HIGH_CARD;
		
	}
	/**
	 * 判断是否为顺子(对按大到小排好序的扑克数组)
	 * @param pokerCards
	 * @return
	 */
	private boolean isStraight(PokerCard [] pokerCards){
		Log.d("POKERHANDS","isStraight");
		if(null == pokerCards || pokerCards.length < HAND_NUM)
			return false;
		int start = 0;
		int count = 1;
		for(int i=1; i<pokerCards.length; i++){
			if(pokerCards[i].value == pokerCards[i-1].value){
				continue;
			}
			if(pokerCards[i].value+count== pokerCards[start].value){
				count++;
				/*A,2,3,4,5*/
				if(count == HAND_NUM-1 && pokerCards[start].value == 5 && pokerCards[0].value == 14){
					copyPokers(pokerCards,myCards,start,count,true);
					myCards[4] = pokerCards[0];
					myType = STRIGHT;
					return true;
				}else if(count == HAND_NUM){
					copyPokers(pokerCards,myCards,start,count,true);
					myType = STRIGHT;
					return true;
				}
			}else{
				if(pokerCards.length - i < HAND_NUM-1){ //后面的牌小于手牌的数量，则不可能是顺子
					return false;                       //(考虑A,2,3,4,5)  
				}
				start = i;
				count = 1;
			}
		}
		return false;
	}
	
	/**
	 * 判断顺子
	 * @param pokerCards 排好序的扑克数组
	 * @param start      判断的起始位置
	 * @param length     判断的长度
	 * @param lenCard    顺子的长度
	 * @return
	 */
	public boolean isStraight(PokerCard[] pokerCards,int start,int length ,int lenCard){
		if(null == pokerCards || pokerCards.length < HAND_NUM || start < 0)
			return false;
		int count = 1;
		for(int i=1+start; i<start + length; i++){
			if(pokerCards[i].value == pokerCards[i-1].value)
				continue;
			if(pokerCards[i].value+count== pokerCards[start].value){
				count++;
				/*A,2,3,4,5,...*/
				if(count == lenCard-1 && pokerCards[start].value == lenCard && pokerCards[0].value == 14){
					return true;
				}else if(count == lenCard){
					return true;
				}
			}else{
				if(pokerCards.length - i < lenCard-1){ //后面的牌小于手牌的数量，则不可能是顺子
					return false;						//(考虑A,2,3,4,5)  
				}
				start = i;
				count = 1;
			}
		}
		return false;
	}
	
	/**
	 * 判断顺子
	 * @param pokerCards 排好序的扑克数组
	 * @param start      判断的起始位置
	 * @param length     判断的长度
	 * @param lenCard    顺子的长度
	 * @param color      顺子的花色
	 * @return int       顺子的起始位置(-1代表不是顺子)
	 */
	private int isStraight(PokerCard[] pokerCards,int start,int length ,int lenCard,int color){
		Log.d("POKERHANDS","isStraight COLOR");
		if(null == pokerCards || pokerCards.length < HAND_NUM || start <0 || start + length >pokerCards.length)
			return -1;
		int count = 0;
		for(int i=start,sum=start + length; i< sum; i++){
			if(i>0 && pokerCards[i].value == pokerCards[i-1].value)
				continue;
			if(pokerCards[i].value+count == pokerCards[start].value && pokerCards[i].color == color){
				count++;
				/*A,2,3,4,5,...A的值为14*/
				if(count == (lenCard-1) && pokerCards[start].value == lenCard && pokerCards[0].value == 14 && pokerCards[0].color == color){
					Log.d("POKERHANDS","A,2,3,4,5");
					return start;
				}else if(count == lenCard){
					return start;
				}
			}else{
				start = i;
				count = 1;
				if(length - start  < lenCard-1){ //后面的牌小于手牌的数量，则不可能是顺子
					Log.d("POKERHANDS"," i = " + i);
					return -1;
				}
			}
		}
		return -1;
	}
	
	/**
	 * 判断是否为同花顺（包括皇家同花顺）
	 * @param pokerCards
	 * @return
	 */
	private boolean isStraightFlush(PokerCard[] pokerCards){
		if(flushColor == 0)
			return false;
		Log.d("POKERHANDS","isStraightFlush");
		int pos;
		if((pos = isStraight(pokerCards,0,pokerCards.length,HAND_NUM,flushColor)) > -1){
			if(pos == 0 && pokerCards[pos].value == 14){//皇家同花顺
				myType = ROYAL_FLUSH;
				copyPokers(pokerCards,myCards,pos,HAND_NUM,true);
				return true;
			}else if(pokerCards[pos].value == HAND_NUM && pokerCards[0].value == 14){
				myType = STRIGHT_FLUSH;    //A,2,3,4,5
				copyPokers(pokerCards,myCards,pos,HAND_NUM-1,true);
				myCards[HAND_NUM-1]=pokerCards[0];
				return true;
			}else{
				myType = STRIGHT_FLUSH;  
				copyPokers(pokerCards,myCards,pos,HAND_NUM,true);
				return true;
			}
		}
		return false;
	}
	
	/**
	 * 扑克数组的复制
	 * @param src    源数组
	 * @param dest   目的数组
	 * @param start  源数组起始位置
	 * @param length 复制长度
	 */
	private void copyPokers(PokerCard[] src,PokerCard[] dest,int start,int length){
		if(null == src || null == dest || src.length < start + length || dest.length < length)
			return;
		for(int i = 0;i<length; i++){
			dest[i] = src[start+i];
		}
			
	}
	
	/**
	 * 扑克数组的复制
	 * @param src    源数组
	 * @param dest   目的数组
	 * @param start  源数组起始位置
	 * @param length 复制长度
	 * @param notsame  相同的牌是否跳过
	 */
	private void copyPokers(PokerCard[] src,PokerCard[] dest,int start,int length,boolean notsame){
		Log.d("POKERHANDS","copyPokers notsame");
		if(null == src || null == dest || src.length < start + length || dest.length < length || start < 0)
			return;
		int count = 0;
		while(count < length){
			if(notsame && start > 0 &&src[start-1].value == src[start].value){
				start++;
				continue;
			}
			dest[count] = src[start];
			count++;
			start++;
		}
	}
	
	/**
	 * 从扑克数组中取出五张最大的同花
	 * @param pokerCards 按从大到小排好序的扑克数组
	 */
	private void filterFlush(PokerCard[] pokerCards) {
		Log.d("POKERHANDS","filterFlush");
		if(pokerCards == null || pokerCards.length < HAND_NUM){
			return;
		}
		int count = 0;
		for(int i = 0;i<pokerCards.length;i++){
			if(pokerCards[i].color == flushColor){
				myCards[count] = pokerCards[i];
				count++;
				if(count >= HAND_NUM)
					break;
			}
		}
		myType = FLUSH;
	}

}
